Бэкдор в XZ

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Бэкдор в XZ
Дата публикации 29 марта 2024[1]
Дата открытия (изобретения) 29 марта 2024[2] и 28 марта 2024[3]
Зависит от XZ Utils[d][4][5]
Официальный сайт tukaani.org/xz-ba… (англ.)
Кем доказано Андрес Фройнд[d][6]
Преступник Jia Tan[d]

Бэкдор в пакете XZ Utils для Linux был выпущен 24 февраля 2024 года[7] и обнаружен 28 марта. Внедряется в SSHD (Secure shell daemon) — часть OpenSSH, службу для Unix-подобных ОС, позволяющую удалённым пользователям выполнять команды консоли. «Отравленный» SSHD выполняет команды консоли от неизвестного центра, владеющего скрытым ключом. Бэкдор пассивен — центр сам должен пройти брандмауэр и подключиться к компьютеру. Угроза была обнаружена и остановлена ещё в передовых нестабильных выпусках, до того, как был нанесён масштабный урон.

Предварительно откомпилированный вредоносный код для архитектуры x64 тайно запрятан в динамическую библиотеку сжатия данных LibLZMA. Хакер два года втирался в доверие, пытаясь взять на себя часть работы программиста-одиночки, глубоко знает архитектуру Linux и работу утилит сборки.

Бэкдор получил идентификатор CVE-2024-3094 и максимальный балл серьёзности 10,0.

Это классическая атака на цепочки поставки[en]: вместо того, чтобы атаковать сам OpenSSH, атаковали менее защищённого поставщика. Как отмечает The Economist, бэкдор в XZ Utils считается первой известной атакой на менее защищённые части критичной открытой программы, но это не значит, что атака была действительно первой — и вряд ли будет последней[8].

Инцидент раскрыл множество слабых звеньев в открытом сообществе — организационных, технических и просто ошибок. Хакер не пользовался единой точкой отказа, и даже был вынужден спешить, пока одну из проблем не закрыли[7]. Некоторые слабости были решены сразу же, а другие ещё предстоит решить.

Кто уязвим и как избавиться[править | править код]

Бэкдор рассчитывался на Linux для x64, и успел попасть в Fedora 40[9], мартовские сборки Arch Linux (но он там нерабочий)[10], и в передовые нестабильные каналы обновления[9]. Об атаке также сообщили Kali Linux и OpenSUSE[9].

Не подвержены бэкдору:

  • Другие ОС, не основанные на Unix и x64.
  • FreeBSD с консервативной политикой стабильности, другими методами сборки программ и другой стандартной библиотекой Си[11].
  • Сборки, в которых SSHD не зависит от LibSystemd: Arch Linux, Gentoo и NixOS[7].
  • Ubuntu с консервативной политикой стабильности: она не успела обновиться.

Чтобы избавиться от бэкдора, нужно откатить пакет xz-utils 5.6.0 или 5.6.1 до 5.4[9] — или обновить до будущей новой версии. Многие производители сделали пакеты-откаты — например, у OpenSUSE он 5.6.1.revertto5.4.

История[править | править код]

Предпосылки[править | править код]

Утечка 1998 года говорила, что Linux — враг Microsoft, но компания времён 2020-х даже платит программистам за ПО для Linux.

Сборка программы из человекоредактируемых исходных файлов — это всегда большая сложность: процесс длится долго, отличается для игр и библиотек, для Windows и Linux, на разработческой машине и на выпуск. Не зря инженер по выпуску[en] — отдельная специальность в разработке крупного ПО. Скрипты сборки считаются чем-то вторичным и не проходят такую ревизию, как исходные тексты: выходит действующая программа — и хорошо[12].

В любом ПО признак хорошего тона, а для копилефт-лицензий (GPL) даже требование — зафиксировать (например, в архиве), из каких исходных файлов собран пакет — и скрипт сборки тоже. Если процесс выпуска плохо налажен, или сборка неофициальная, в репозитории и зафиксированных исходных текстах бывает разный скрипт сборки[7]. Новаторством стало то, что эти изменения — малая часть вредоносного кода.

Репозитории библиотек, работающих с файлами какого-то формата (например, с архивами), содержат немалое количество тестовых архивов, взявшихся из разных источников — найденных у пользователей, созданных вручную или какой-то утилитой[13]. Для многих из этих файлов нет лучшего «исходника», чем сами файлы. Там и спрятали основную часть зловреда.

В 2020 году[14] веб-комикс xkcd написал: вся инфраструктура современных IT где-то в глубине держится на программе, бескорыстно поддерживаемой парнем из Небраски[15] — тем более прецеденты были: в 2016 году турецко-американский программист Азер Кочулу, получив от авторов программы Kik Messenger требование отдать имя Kik и поняв, что NPM на их стороне, удалил из NPM все свои пакеты, включая тривиальную функцию leftpad, на несколько часов парализовав сверхпопулярный React — и, по странному совпадению, Kik Messenger тоже[16]. Именно таким — поддерживаемым одним человеком в свободное время — оказался пакет XZ Utils[en], позволивший открытому ПО сжимать не хуже WinRAR. Нумерация версий у него по старой системе Linux «чёт-нечет»: у стабильных вторая цифра чётная, у тестовых — нечётная.

Есть расширение формата ELF под названием косвенная функция (IFUNC)[17] — у некоторых важных подпрограмм вроде memcpy есть несколько версий: скажем, общая и оптимизированная под набор команд SSE4. Загрузчик вызывает специальную функцию (resolver), та проверяет возможности процессора и возвращает ссылку на одну из версий, ещё до подстановки адресов в машинный код главной программы. Прикладной программе не нужна даже перекомпиляция. Этот resolver вызывается рано (при загрузке программы) и всегда (даже если ни одной функции главной программе не нужно).

Программисты часто отказываются от библиотеки, особенно в форме динамического модуля (*.dll/*.so), по собственным причинам: слишком велика, недостаточно кроссплатформенна, или ещё по каким-то. Разработчики дистрибутивов своими силами налаживали передачу в систему информации о работе службы SSHD через функцию libsystemd.sd_notify[18], но OpenSSH упорно не принимали LibSystemd[19][a]. Хоть часть задачи — автоматический перезапуск «упавшей» службы — попытались решить окольным путём в конце 2016, но отказались из-за гонок за файл[19].

Путь в разработчики[править | править код]

Осенью 2021 на GitHub появился пользователь с ником JiaT75, подписывавшийся как Jia Tan (в дальнейшем будем звать его Цзя). Первое же его изменение в библиотеке LibArchive было крайне подозрительным: вместо внутренней функции safe_fprintf он использовал fprintf[20] (поскольку архиваторы часто вызываются из терминала и выводят много пользовательского текста, «безопасная» версия обходила ошибки в терминальных программах[21]). Изменение приняли. Тогда же он вносит первые мелкие изменения (стилевые настройки, воспроизводимость результатов) в XZ Utils[7].

Весной 2022 Цзя вносит изменение в Java-версию XZ Utils. Сразу же появляются несколько «пользователей», желавших заполучить это изменение, и Цзя в грубой форме требует, чтобы финн Лассе Коллин (Lasse Collin), единственный человек, поддерживавший проект, передал ему управление, раз не справляется с потоком проблем. В ответ Лассе ссылается на депрессию[7].

С января 2023 Цзя всё больше и больше включается в работу над XZ[20]. В частности, он объявляет себя контактным лицом проекта.

В июне 2023 Цзя включает первую косвенную функцию — CRC64[22] для новых процессоров (Intel с 2010, AMD с 2013). Дальнейшая активность автора функции, Ханса Янсена, незначительна[23]. Неизвестно, насколько это изменение нужно пользователям, ведь быстрая (не криптографическая) контрольная сумма — не самое критичное место библиотеки сжатия.

В январе 2024 Цзя перемещает главный сайт на GitHub Pages — единственный ресурс, к которому имеет доступ[24].

Атака[править | править код]

23 февраля 2024 года Цзя вносит несколько тестовых архивов, два из них содержат бэкдор[20][25], и собирает версию 5.6.0. В зафиксированных исходных текстах в скрипте сборки оказываются 26 лишних строк по сравнению с Git[18]. Сразу нашлись две ошибки, стабильность Gentoo Linux и срабатывание Valgrind (программы поиска ошибок доступа к памяти) прямо на вредоносной функции у Red Hat — бэкдор нужно исправить, пока не обнаружили[7].

Тем временем в LibSystemd появляется изменение: загружать редко нужную LibLZMA динамически, когда она нужна, не загрузчиком, а функцией dlopen, чтобы уменьшить размер стартового виртуального «диска» (механизм начальной загрузки Linux, позволяющий разворачивать ПО, пока ещё не загрузилась поддержка файловых систем). Это должно было сломать бэкдор (LibLZMA выпадает из зависимостей SSHD), и начинается ещё одна гонка: кто первым выложит изменение в общий канал?[7]

После двух недель разработки ошибки поддаются, и 9 марта Цзя обновляет два тестовых архива, крайне подозрительно описав правку[7][26] — это будет версия 5.6.1. Кроме того, хакер понимает, что второй раз обновить тестовый архив — это уже чересчур, и он вносит туда поиск обновлений[13]. Заодно он делает пустое изменение, якобы исправляющее Valgrind[7].

В конце марта 2024 появляется упомянутый Ханс Янсен и просит побыстрее внести 5.6.1 в Debian[7].

28 марта Цзя склоняет Ubuntu взять 5.6.1, Ubuntu скептична — изменения не такие важные. Но не успевает — через несколько часов Debian откатывают, а на следующий день по открытому сообществу прокатывается известие[27].

Обнаружение[править | править код]

28 марта 2024 года программист из Microsoft Андрес Фройнд (Andres Freund) предупреждает о бэкдоре производителей Linux[7][27], 29 марта — всех[28].

Он испытывал PostgreSQL на производительность на новейшем нестабильном выпуске Debian Linux и обнаружил замедление: подключение по SSH занимало не 0,3, а 0,8 с[29]. Андрес пришёл к выводу, что виноват пакет XZ Utils версий 5.6.0 и 5.6.1, вспомнил срабатывание Valgrind две недели назад и отследил почти все факты:

  • это наверняка бэкдор;
  • какая функция виновата, где спрятана и как попадает в скомпилированный пакет;
  • при каких условиях срабатывает;
  • кто хакер — по его мнению, Цзя именно хакер под прикрытием, а не взломанный разработчик.

Что делает функция и как она встала на место OpenSSL — для него осталось загадкой.

После обнаружения[править | править код]

Лассе Коллин, руководитель проекта, сделал периодически обновляемый пресс-релиз, содержащий только сухие факты[30]. Блогеры похвалили его за такую реакцию.

GitHub закрыл проект и все его зеркала, и забанил обоих пользователей. Лассе вернул домашнюю страницу на личный сервер, и там же развернул Git-репозиторий проекта. Лассе разбанили 2 апреля, а 9 апреля проект снова переехал на GitHub. GitHub закрыл все запросы на изменения (pull requests), как будто их закрыл Лассе, а когда Лассе стал снова их открывать, чтобы проверить и внести — снова автоматически закрывал[30].

Авторы дистрибутивов откатили XZ до 5.4[9] и исключили Цзя из доверенных сборщиков. Лассе обещает, что новая чистая версия будет иметь номер 5.8.

Провели более глубокий аудит изменений Цзя и откатили многие его изменения, включая[31] LibArchive.

Началась волна сокращения цепочек поставки в критичном ПО, в частности:

  • В OpenSSH написали автономную функцию sd_notify, чтобы авторы дистрибутивов не делали патчи с посторонними зависимостями[19]. Проблема стояла с 2016 года, но занялись ею только после бэкдора. Несколько позже LibSystemd написала свой вариант автономной sd_notify[18] — для всех, кому библиотека нужна только ради этой функции.
  • Начались разговоры об отказе LD (стандартного линкера GNU) от JSON-библиотеки[18] — но поскольку ошибки в метаданных исполняемого файла Linux и неадекватная реакция ПО на них возможны уже сейчас, а атака на LibJanssen будет не скоро, до дела не дошло. Альтернативные скоростные линкеры LLD и MOLD смотрят на JSON как на чёрный ящик, никак не проверяя, да и LD можно собрать в такой конфигурации[32].

Ubuntu отложила на неделю апрельскую бета-версию, и своими силами пересобрала все пакеты[33].

Личность Цзя[править | править код]

Имя Jia Cheong Tan вряд ли бывает у жителя континентального Китая[b]. Нашли некоего Jia Tan из Калифорнии, но исследователи больше склоняются к краже имени[23].

Заявленный часовой пояс — UTC+8 (Восточная Азия), IP-адрес — сингапурский VPN, что ни о чём не говорит. Основная часть правок сделана в 12…17 часов UTC[c][23], что похоже на рабочее время UTC+2 или +3 (Европа от Греции до России, Ближний Восток, часть Африки)[34]. Девять правок сделаны с заявленным часовым поясом UTC+2/3[34] — возможно, финн Лассе принял изменения своими руками, или хакер забыл переключить часовой пояс. Работал в китайские праздники и не работал в европейские[d][34].

Принцип действия[править | править код]

Бэкдор устроен очень сложно. Окольными путями решаются два вопроса: как протащить вредоносный код в открытый проект, и как его подключить к SSHD, который LZMA вообще не вызывает, даже в тех дистрибутивах, где есть зависимость SSHD ← LibSystemd ← LibLZMA.

Внедрение вредоносного кода в LibLZMA[править | править код]

  1. Скрипт сборки, лежащий в зафиксированных исходных текстах (не в Git!), находит пример плохого архива tests/files/bad-3-corrupt_lzma2.xz (без явного указания этого имени![13]), четырьмя заменами (TAB↔пробел, дефис↔подчерк) превращает его в хороший, и распаковывает более старым скомпилированным XZ[35]. На выходе получается командный файл.
  2. Этот файл распаковывает второй пример, на сей раз хорошего архива — tests/files/good-large_compressed.lzma — 1 килобайт распакованных данных пропускается, 2 читаются, 1 снова пропускается…
  3. То, что получилось, обрабатывается шифром простой замены и снова разархивируется. В результате снова получается командный файл, который и прислал Фройнд[36].
  4. В этом командном файле были какие-то скрипты на будущее — например, поиск обновлений в других тестах. А если обновлений нет — из тех же тестовых файлов сходным образом извлекается заранее откомпилированный объектный файл liblzma_la-crc64-fast.o и расшифровывается RC4, написанным на препроцессоре AWK (!).
  5. Кроме того, корректирует два связанных с косвенными функциями Си-файла (CRC64 и CRC32), чтобы они вызывали вредоносную функцию _get_cpuid[13], а не системную __get_cpuid[37]. Одного файла хватало, но, видимо, хакер хотел, чтобы выбор обеих косвенных функций выглядел в дизассемблере одинаково[13].

Объектный файл занимает 86 килобайт[38], что многовато для одной оптимизированной функции. В действительности функций там несколько десятков, и все переименованы так, чтобы не вызывать подозрение[39]: одна из таблиц префиксного дерева называется Llzip_decode_1.

Командные файлы Цзя полагались на ошибки в CMake[40] и Autoconf[41][18].

Внедрение вредоносного кода из LibLZMA в SSHD[править | править код]

На конец апреля 2024 бэкдор полностью исследован[42], несмотря на определённую защиту от исследования: например, условия if (variable=="string") записаны не открытым текстом, а в виде префиксного дерева: if findInTrie(variable)==stringId[43].

Во многих дистрибутивах SSHD неофициально подключает функцию sd_notify из библиотеки LibSystemd[18], чтобы передавать другому ПО, в каком состоянии служба. Эта библиотека совсем в других функциях полагается на LibLZMA.

Подпрограмма выбора косвенных функций crc64_resolve находится в Си-файле с оптимизированными функциями — она должна при загрузке выбрать версию функции CRC64. После изменений, внесённых в на этапе сборки, она вызывает вредоносную _get_cpuid[13], та, если функция запущена из программы /usr/sbin/sshd[28] и в переменных среды отсутствует код отключения, делает две вещи[43].

  1. Ищет в памяти процесса образы других ELF-модулей и, не требуя явно OpenSSL, находит в памяти указатели на его шифровальные функции[43].
  2. Во-вторых, окольным путём (меняя память функции, нужной, чтобы программа могла следить за процессом динамической компоновки) заменяет три функции OpenSSL на вредоносные[43]. Наиболее известна из них RSA_public_decrypt, попавшая в отчёт Фройнда[28], но остальные две делают то же самое[42].

Бэкдор[править | править код]

RSA_public_decrypt запускается ещё до того, как проверит имя пользователя — на стадии обмена ключами. Ключ шифрования и является командой для бэкдора.

Функция пробует расшифровать ключ симметричным шифром ChaCha20 (команда зашифрована, чтобы походила на ключ), делает несложную проверку заголовка, а потом проверяет электронную подпись, сделанную шифром Ed448. Если подпись сходится, команда выполняется. Подтверждена такая вредоносная функциональность[42]:

  • Исполнить любую команду консоли функцией system(). Подменив открытый ключ на собственный, блогеры повторяли это поведение[44].
  • Следующее подключение не будет проверять пароль.
  • Отключить на время часть логов SSHD.
  • Включить/выключить отладочный вывод самого себя.
  • Отключить Pluggable Authentication Modules.

Поднятые вопросы[править | править код]

В очередной раз после Heartbleed и NPM/React получилось, что критичная инфраструктура оказалась в руках неоплачиваемого хобби-разработчика. Как отмечает эксперт Михал Залевский (Michal Zalewski)[29]:

Вывод состоит в том, что несказа́нные триллионы долларов опираются на программный код, написанный любителями. В программном обеспечении интернета могут скрываться и другие пока не обнаруженные лазейки.

Снова после утечек Сноудена поднят вопрос о государственных хакерах, внедрившихся в открытое сообщество, тем более в хакере есть признаки жителя Восточной Европы на зарплате[34]. По мнению The Economist, выдержка, проявленная Цзя, а также поддержка некоторых других аккаунтов, рекомендовавших Коллину передать свою работу новичку, указывают на операцию государственной разведслужбы[29].

Издание также отмечает, что атаки на цепочки поставки[en] становятся всё более частыми. Так, в 2019—2020 годах российская служба внешней разведки проникла в сети правительства США через платформу управления сетями SolarWinds Orion. Хакеры на службе правительства КНР вмешались в прошивку роутеров Cisco с целью получения доступа к экономическим, коммерческим и военным объектам США и Японии[8].

Из технических вопросов — прозрачность инфраструктуры сборки (например, почему в скрипте сборки функции eval и AWK?)[45], возможные атаки на чужую память (из косвенных функций и не только) в другом ПО.

Примечания[править | править код]

Комментарии
  1. Причиной указано «пустить верблюжий нос в шатёр» — поговорка, основанная на сказке: бедуин пустил в шатёр сначала нос, потом голову, и так до целого верблюда. По-русски «дай палец, руку откусит» или «коготок увяз — всей птичке пропасть».
  2. Это смесь языков Китая (мандарина, кантонского и южноминьского), к тому же в континентальном Китае любят склеивать два личных имени в одно[23][34]
  3. Статистика собрана по событиям GitHub, независимо от типа — а не по заявленному времени правок.
  4. Работал: в китайский Новый год (с 22 до 27 января 2023), День поминовения умерших (5 апреля 2023), Праздник осени (29 сентября 2023). Не работал: оба католических Рождества (25 декабря 2022/23), оба григорианских Новых года (31 декабря 2022/23, 1 января 2023/24).
Источники
  1. https://www.cve.org/CVERecord?id=CVE-2024-3094
  2. https://www.openwall.com/lists/oss-security/2024/03/29/4
  3. https://research.swtch.com/xz-timeline
  4. https://tukaani.org/xz-backdoor/
  5. https://www.cisa.gov/news-events/alerts/2024/03/29/reported-supply-chain-compromise-affecting-xz-utils-data-compression-library-cve-2024-3094 (англ.)
  6. https://www.nytimes.com/2024/04/03/technology/prevent-cyberattack-linux.html
  7. 1 2 3 4 5 6 7 8 9 10 11 12 research!rsc: Timeline of the xz open source attack
  8. 1 2 A chilling near-miss shows how today’s digital infrastructure is vulnerable, The Economist, 4 April 2024
  9. 1 2 3 4 5 Red Hat warns of backdoor in XZ tools used by most Linux distros
  10. Arch Linux — News: The xz package has been backdoored
  11. Disclosed backdoor in xz releases — FreeBSD not affected
  12. Understanding the Linux Backdoor: Implications for Open Source [When Penguins Cry] — YouTube
  13. 1 2 3 4 5 6 research!rsc: The xz attack shell script
  14. 2347: Dependency — explain xkcd
  15. xkcd: Dependency
  16. How one programmer broke the internet by deleting a tiny piece of code
  17. GNU indirect function | MaskRay
  18. 1 2 3 4 5 6 xz-utils backdoor situation (CVE-2024-3094) · GitHub
  19. 1 2 3 2641 — Add systemd notify code to to track running server
  20. 1 2 3 The XZ Backdoor: Everything You Need to Know | WIRED
  21. Added error text to warning when untaring with bsdtar by JiaT75 · Pull Request #1609 · libarchive/libarchive · GitHub
  22. git.tukaani.org — xz.git/commit
  23. 1 2 3 4 Evan Boehs. Everything I Know About the XZ Backdoor
  24. git.tukaani.org — xz.git/commitdiff
  25. git.tukaani.org — xz.git/commitdiff
  26. git.tukaani.org — xz.git/commitdiff
  27. 1 2 Bug #2059417 «Sync xz-utils 5.6.1-1 (main) from Debian unstable …» : Bugs : xz-utils package : Ubuntu
  28. 1 2 3 oss-security — backdoor in upstream xz/liblzma leading to ssh server compromise
  29. 1 2 3 A stealth attack came close to compromising the world’s computers, The Economist, Apr 2nd 2024
  30. 1 2 XZ Utils backdoor
  31. tar: make error reporting more robust and use correct errno by emaste · Pull Request #2101 · libarchive/libarchive · GitHub
  32. Remove dependency on libjansson
  33. Noble Numbat Beta delayed (xz/liblzma security update) - Announcements - Ubuntu Community Hub
  34. 1 2 3 4 5 XZ Backdoor: Times, damned times, and scams
  35. xz/liblzma: Bash-stage Obfuscation Explained — gynvael.coldwind//vx.log
  36. https://www.openwall.com/lists/oss-security/2024/03/29/4/1
  37. CPUID — OSDev Wiki
  38. xzre/liblzma_la-crc64-fast.o at main · smx-smx/xzre · GitHub
  39. [WIP] XZ Backdoor Analysis and symbol mapping · GitHub
  40. Consider hardening check_c_source_compiles (#25846) · Issues · CMake / CMake · GitLab
  41. autoreconf -force seemingly does not forcibly update everything
  42. 1 2 3 binary-risk-intelligence/xz-backdoor at master · binarly-io/binary-risk-intelligence · GitHub
  43. 1 2 3 4 Kaspersky analysis of the backdoor in XZ | Securelist
  44. revealing the features of the XZ backdoor — YouTube
  45. Three steps we could take to make supply chain attacks a bit harder — devel — Fedora Mailing-Lists